#include "SQLitePreferenceDAO.h"

SQLitePreferenceDAO::SQLitePreferenceDAO(sqlite3* database) {
	this->database = database;
}

void SQLitePreferenceDAO::save(const Preference& preference) {
	sqlite3_stmt* statement;
	const char* sql = "insert into preference values (?,?,?,?)";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_null(statement, 1);
	sqlite3_bind_int(statement, 2, preference.getContent().getId());
	sqlite3_bind_int(statement, 3, preference.getUser().getId());
	sqlite3_bind_int(statement, 4, preference.getRating());

	rc = sqlite3_step(statement);
	sqlite3_finalize(statement);
	if (rc != SQLITE_DONE) {
		std::ostringstream oss;
		oss << "Cannot save the preference. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}
}

void SQLitePreferenceDAO::update(const Preference& preference) {
	sqlite3_stmt* statement;
	const char* sql =
			"update preference set content_id=?,user_id=?,rating=? where id=?";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_int(statement, 1, preference.getContent().getId());
	sqlite3_bind_int(statement, 2, preference.getUser().getId());
	sqlite3_bind_int(statement, 3, preference.getRating());
	sqlite3_bind_int(statement, 4, preference.getId());

	rc = sqlite3_step(statement);
	sqlite3_finalize(statement);
	if (rc != SQLITE_DONE) {
		std::ostringstream oss;
		oss << "Cannot update the preference. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}
}

void SQLitePreferenceDAO::remove(const Preference& preference) {
	sqlite3_stmt* statement;
	const char* sql = "delete from preference where id=?";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_int(statement, 1, preference.getId());

	rc = sqlite3_step(statement);
	sqlite3_finalize(statement);
	if (rc != SQLITE_DONE) {
		std::ostringstream oss;
		oss << "Cannot remove the preference. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}
}

Preference SQLitePreferenceDAO::loadPreferenceById(int id) {
	sqlite3_stmt* statement;
	const char* sql = "select * from preference where id=?";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_int(statement, 1, id);

	rc = sqlite3_step(statement);
	if (rc != SQLITE_ROW) {
		if (rc == SQLITE_DONE)
			return Preference();
		std::ostringstream oss;
		oss << "Cannot load the preference. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	DAOFactory* daoFactory = DAOFactory::getDAOFactory();
	int pid = sqlite3_column_int(statement, 0);
	Content content = daoFactory->getContentDAO()->loadContentById(
			sqlite3_column_int(statement, 1));
	User user = daoFactory->getUserDAO()->loadUserById(sqlite3_column_int(
			statement, 2));
	int rating = sqlite3_column_int(statement, 3);

	//change
	//TODO
	Preference preference(pid, content, user, rating, 0);

	sqlite3_finalize(statement);

	return preference;
}

std::list<Preference> SQLitePreferenceDAO::loadUserPreferences(const User& user) {
	sqlite3_stmt* statement;
	const char* sql =
			"select id,content_id,rating from preference where user_id=?";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_int(statement, 1, user.getId());

	std::list<Preference> preferences;
	DAOFactory* daoFactory = DAOFactory::getDAOFactory();
	Content content;
	int rating;
	int pid;

	while (true) {
		rc = sqlite3_step(statement);
		if (rc != SQLITE_ROW) {
			if (rc == SQLITE_DONE)
				break;
			std::ostringstream oss;
			oss << "Cannot load the preference. SQLite code: " << rc;
			throw SQLiteException(oss.str());
		}

		pid = sqlite3_column_int(statement, 0);
		content = daoFactory->getContentDAO()->loadContentById(
				sqlite3_column_int(statement, 1));
		rating = sqlite3_column_int(statement, 2);

		//change
		//TODO
		preferences.push_back(Preference(pid, content, user, rating, 0));
	}

	sqlite3_finalize(statement);

	return preferences;
}

std::list<Preference> SQLitePreferenceDAO::loadAll() {
	sqlite3_stmt* statement;
	const char* sql = "select * from preference";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	std::list<Preference> preferences;
	DAOFactory* daoFactory = DAOFactory::getDAOFactory();
	Content content;
	User user;
	int rating;
	int pid;

	while (true) {
		rc = sqlite3_step(statement);
		if (rc != SQLITE_ROW) {
			if (rc == SQLITE_DONE)
				break;
			std::ostringstream oss;
			oss << "Cannot load the preference. SQLite code: " << rc;
			throw SQLiteException(oss.str());
		}

		pid = sqlite3_column_int(statement, 0);
		content = daoFactory->getContentDAO()->loadContentById(
				sqlite3_column_int(statement, 1));
		user = daoFactory->getUserDAO()->loadUserById(sqlite3_column_int(
				statement, 2));
		rating = sqlite3_column_int(statement, 3);

		preferences.push_back(Preference(pid, content, user, rating, 0));
	}

	sqlite3_finalize(statement);

	return preferences;
}
